Connectivity Software User's Guide and Reference
OPC Wizard Permission Authorization
OPC Wizard > Concepts > OPC Wizard Features > OPC Wizard Access Control > OPC Wizard Permission Authorization
In This Topic

Introduction 

In the permission authorization step, the security roles of the user are checked to verify that the user is granted the necessary permission to perform the operation on the given entity. If the user’s role includes the required permissions for the requested action, access is granted; otherwise, it is denied.

OPC Wizard allows the developer to specify permission assignment on every custom server node. The permission assignment specifies the permissions granted to individual roles. In addition, permissions can be inherited from the parent node or from the default for the namespace. If you have special needs, the permission checking mechanism can also be customized in the code.

Permissions

Permissions define the actions allowed on resources (entities). In OPC Wizard, permissions are specified using the UAPermissions Enumeration. Flags from this enumeration can be combined together. Following are the individual OPC UA permissions defined by the enumeration:

Member Value Description
AddNode 65536 Add node.                
AddReference 8192 Add reference.                
Browse 1 Browse.                
Call 4096 Call.                
DeleteHistory 1024 Delete history.                
DeleteNode 32768 Delete node.                
InsertHistory 256 Insert history.                
None 0 None.                
Read 32 Read.
ReadHistory 128 Read history.
ReadRolePermissions 2 Read role permissions.
ReceiveEvents 2048 Receive events.
RemoveReference 16384 Remove reference.
Write 64 Write.
WriteAttribute 4 Write attribute.
WriteHistorizing 16 Write historizing.
WriteRolePermissions 8 Write role permissions.

 OPC Wizard also predefines some useful combinations:

Member Value Description
All -1 All permissions.
EditHistory 1792 Permissions for editing history (insert, modify, and delete).
ModifyAll 128860 All modification permissions including attributes, role permissions, historizing, value writes, history editing, calls, references, and node operations.
ModifyBasic 4160 Basic modification permissions (write values and call methods).
ModifyRegular 4180 Regular modification permissions including attributes, historizing, value writes, and method calls.
ModifySecurity 4172 Security-related modification permissions including attributes, role permissions, value writes, and method calls.
ViewAll 2211 All viewing permissions including browse, role permissions, value reads, history reads, and event reception.
ViewBasic 33 Basic viewing permissions (browse and read values).
ViewRegular 2209 Regular viewing permissions including browse, value reads, history reads, and event reception.
ViewSecurity 35 Security-related viewing permissions including browse, value reads, and role permissions.

Permission Assignment

The permission assignment defines which permissions are actually granted by specific security roles. In OPC Wizard, role assignment is represented by an instance of UAPermissionAssignment Class. This object is a collection of UARolePermissions object, each of which contains a role ID and the corresponding permissions, represented by flags from UAPermissions Enumeration. The UAPermissionAssignment Class is a keyed collection where the key is the role ID. For a user that is in some security roles (given by their role IDs), his permissions given by the permission assignment will be a union of the permissions in the role assignment for the security roles the user is in.

You can construct the permission assignments in the code yourself, or you can use one of the predefined permission assignments, available as static properties on the UAPermissionAssignment Class:

Name Description
Public Propertystatic (Shared in Visual Basic) Permission assignment that allows anonymous users to view and authenticated users to view and modify.  
Public Propertystatic (Shared in Visual Basic) An empty permission assignment with no role permissions.  
Public Propertystatic (Shared in Visual Basic) Permission assignment that gives full access to everyone (anonymous users).
Public Propertystatic (Shared in Visual Basic) Permission assignment based on the standard OPC UA roles with their base permissions.  
Public Propertystatic (Shared in Visual Basic) Permission assignment that allows anonymous users to view and trusted applications to view and modify.  

Permission Assignment for Nodes

Each custom server node (represented by an instance of UAServerNode Class) has a PermissionAssignment Property which can be used to control access to the node and operations on the node.  This property contains a UAPermissionAssignment object described above. By confuring this property, you define the type of access the users with various roles will have to the node.

OPC Wizard takes care of checking whether the user performing any operation has the necessary permission. If the user does not have the permission, the operation is automatically rejected by the OPC Wizard, without further need for coding on your side - all that's needed is to configure the right permission assignments.

For custom nodes under the Objects folder, the default value of PermissionAssignment Property is Empty. This does not mean, however, that such server nodes will not be accessible at all, because OPC Wizard combines this explicitly defined property assignment with an inherited one - see further below.

Permission Assignment Inheritance

The OPC Wizard evaluates whether the user has the necessary permission using the effective permission assignment. The effective permission assignment on a server node is calculated by combining (making a union of) the explicitly defined permission assignments on the node (the PermissionAssignment Property) with base permission assignment. This mechanisms allows the permission assignment be inherited from the namespace or the parent node, making it easier to define permissions on larger node hierarchies.

The base permission assignment, i.e. what is inherited, depends on the setting of the PermissionsInheritanceType Property on the server node, and can be one of the following:

Member Value Description
Namespace 1 Permissions are inherited from the namespace.
None 0 No permission inheritance. The node uses only its own permission assignment.
ParentNode 2 Permissions are inherited from the parent node. This is the default inheritance type.

The default permissions inheritance type for custom nodes under the Objects folder is ParentNode. The Objects node itself, however, has its permissions inheritance type  set to Namespace by default.

For server nodes defined in the Objects folder hierarchy, "Inheriting from the namespace" means that the base permission assignment is taken from the ObjectsNamespaceDefaultPermissionAssignment Property on the EasyUAServer object.

Your code can make use of these defaults e.g. in following ways:

Simply changing the ObjectsNamespaceDefaultPermissionAssignment Property assigns the specified permission to all custom nodes in the Objects folder hierarchy that have not defined a different permisssions inheritance type.

Changing the PermissionAssignment Property on any node in the Objects folder hierarchy defines the permission assignment for this node, and also for all its children, recursively (all descendants), unless the permissions inheritance type has been changed on the descendant.

Permisssion assignment, including inheritance, is illustrated in the example below.

.NET

// This example shows how to create a hierarchical permission assignment in an OPC UA server, for Anonymous, Engineer and
// Operator roles.
// You can use any OPC UA client, including our Connectivity Explorer and OpcCmd utility, to connect to the server. 
//
// Find all latest examples here: https://www.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://forum.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using OpcLabs.BaseLib.Security.User.Extensions;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.NodeSpace;
using OpcLabs.EasyOpc.UA.Security.Subject;
using System;

namespace UAServerDocExamples.AccessControl
{
    internal class PermissionAssignment
    {
        public static void Hierarchical()
        {
            // Instantiate the server object.
            // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            var server = new EasyUAServer();

            // Clear the default security roles (Operator) for the Anonymous user.
            server.UserManagers.Anonymous.SecurityRoleIdSet.Clear();

            // Create users with Engineer and Operator roles.
            server.UserManagers.NameAndPassword.CreateWithSecurityRoleIds("engineer", "pass", 
                new string[] {UASecurityRoles.Engineer});
            server.UserManagers.NameAndPassword.CreateWithSecurityRoleIds("operator", "pass", 
                new string[] {UASecurityRoles.Operator});

            // Specify the default permission assignment for the custom nodes under Objects folder. Anonymous users will be
            // able to view only, authenticated users will be also able to modify.
            server.ObjectsNamespaceDefaultPermissionAssignment = UAPermissionAssignment.AuthenticateUserToModify;


            // Define some nodes with different permission assignments.

            // This data variable is readable by anyone, but only writable by authenticated users, because it inherits
            // permission assignments from the Objects folder, which further inherits them from the namespace.
            server.Objects.Add(new UADataVariable("PublicReadableDataVariable").ReadWriteValue(0));

            // This data variable will be readable and writable by anyone. It inherits the read permissions for anyone from
            // the Objects folder, and it adds its own permission assignment for write permissions.
            var publicReadWriteDataVariable = new UADataVariable("PublicReadWriteDataVariable").ReadWriteValue(0);
            publicReadWriteDataVariable.PermissionAssignment = new UAPermissionAssignment
            {
                new UARolePermissions(UASecurityRoles.Anonymous, UAPermissions.ModifyBasic)
            };
            server.Objects.Add(publicReadWriteDataVariable);

            // This data variable will only be accessible to authenticated users. Other users will not even be able to see
            // it (browse for it). We specify that it should not inherit permissions from the namespace, but instead have
            // its own permission assignment.
            var authenticatedDataVariable = new UADataVariable("AuthenticatedDataVariable").ReadWriteValue(0);
            authenticatedDataVariable.PermissionAssignment = new UAPermissionAssignment
            {
                new UARolePermissions(UASecurityRoles.AuthenticatedUser, UAPermissions.ViewBasic | UAPermissions.ModifyBasic)
            };
            authenticatedDataVariable.PermissionsInheritanceType = UAPermissionsInheritanceType.None;
            server.Objects.Add(authenticatedDataVariable);

            // This folder will only be accessible to users with Operator or Engineer security roles. User that do not have
            // any of these security roles will not even be able to see it (browse for it).
            var operatorOrEngineerFolder = new UAFolder("OperatorOrEngineerFolder");
            operatorOrEngineerFolder.PermissionAssignment = new UAPermissionAssignment
            {
                new UARolePermissions(UASecurityRoles.Operator, UAPermissions.ViewBasic | UAPermissions.ModifyBasic),
                new UARolePermissions(UASecurityRoles.Engineer, UAPermissions.ViewBasic | UAPermissions.ModifyBasic),
            };
            operatorOrEngineerFolder.PermissionsInheritanceType = UAPermissionsInheritanceType.None;
            server.Objects.Add(operatorOrEngineerFolder);

            // This data variable will inherit permissions from the OperatorOrEngineerFolder, so it will only be accessible to users
            // with Operator or Engineer security roles. 
            var operatorOrEngineerDataVariable = new UADataVariable("OperatorOrEngineerDataVariable").ReadWriteValue(0);
            operatorOrEngineerFolder.Add(operatorOrEngineerDataVariable);

            // A data variable accessible to Operator but not Engineer (typically, live data).
            var operatorDataVariable = new UADataVariable("OperatorDataVariable").ReadWriteValue(0);
            operatorDataVariable.PermissionAssignment = new UAPermissionAssignment
            {
                new UARolePermissions(UASecurityRoles.Operator, UAPermissions.ViewBasic | UAPermissions.ModifyBasic),
            };
            operatorDataVariable.PermissionsInheritanceType = UAPermissionsInheritanceType.None;
            operatorOrEngineerFolder.Add(operatorDataVariable);

            // A data variable accessible to Engineer but not Operator (typically, configuration data).
            var engineerDataVariable = new UADataVariable("EngineerDataVariable").ReadWriteValue(0);
            engineerDataVariable.PermissionAssignment = new UAPermissionAssignment
            {
                new UARolePermissions(UASecurityRoles.Engineer, UAPermissions.ViewBasic | UAPermissions.ModifyBasic),
            };
            engineerDataVariable.PermissionsInheritanceType = UAPermissionsInheritanceType.None;
            operatorOrEngineerFolder.Add(engineerDataVariable);


            // Start the server.
            Console.WriteLine("The server is starting...");
            server.Start();

            Console.WriteLine("The server is started.");
            Console.WriteLine();

            // Let the user decide when to stop.
            Console.WriteLine("Press Enter to stop the server...");
            Console.ReadLine();

            // Stop the server.
            Console.WriteLine("The server is stopping...");
            server.Stop();

            Console.WriteLine("The server is stopped.");
        }
    }
}

Custom Permission Authorization

In advanced scenarios, you can complement the permission authorization behavior described above and implement your own authorization logic. To allow for this, OPC Wizard passes a server operation context (an instance of the UAServerOperationContext Class) to the method events used to implement OPC operations such as reads, writes, and subscriptions. Inside the server operation context, you will find a SecurityPrincipal Property. This security principal encapsulated the identity and role-based security information related to the operation, and allows your code to make security-based decisions. Specifically, among other things, you can

The custom logic to perform permission authorization can be placed to:

If your code determines that the permission should be granted, continue to the base implementation of the method, or do not handle the event. Otherwise, fail the operation with appropriate status code, in most cases BadUserAccessDenied.  

Even when you implement the custom permission authorization, the OPC Wizard's built-in mechanism still remains in effect. Consequently, your authorization logic can only deny access where the built-in mechanism has already granted access. But your authorization logic cannot grant access where the built-in mechanism has already denied it. Your custom logic can make the permissions narrower, not wider. Make sure you define "wide enough" permissions using the built-in permission authorization mechanism, so that your custom logic gets a chance to run and in cases it is supposed to act upon.

See Also